tree grammar vslTree;

options {
    tokenVocab = vsl;
    ASTLabelType = CommonTree;
}

//@header {}

@members {
    TableSymboles tabSymboles = new TableSymboles();
}

/*-----------------------------------------------------------------
 * PARSER RULES
 *----------------------------------------------------------------*/

program returns [Code cod]
    @init { cod = new Code(); }
    :
    ^( PROG
        ( u = unite { cod.append(u); } )+
    )
;

unite returns [Code cod]
    @init { cod = new Code(); }
    : f = function { cod.append(f); }
    | p = proto { cod.append(p); }
;

function returns [Code cod]
    @init { cod = new Code(); }
    :
    ^( FUNC_KW
        t = type i = IDENT
        params = param_list
    {
        // Gestion de l'identifiant de la fonction.
        String name = i.getText();
        Tokatt fun = tabSymboles.Lookup(name);

        if (fun == null) {
            TypeFunction type = new TypeFunction(t);
            type.Proto2Fonc();

            fun = new Tokatt(type, name, 0);
            fun.value = params.size();

            tabSymboles.Insert(name, fun);
        } else {
            TypeFunction ty = (TypeFunction)fun.type;

            if (!ty.IsProto()) {
                System.err.println("error: '" + name
                    + "' previously declared but not as prototype.");
                System.exit(1);
            }

            ty.Proto2Fonc();
        }

        // Generation d'un label et de "beginfunc".
        cod.append(Code.genLabel(fun));
        cod.append(new Inst3a(Inst3a.TAC_BEGINFUNC, null, null, null));
        tabSymboles.Enter_Scope();

        // Declaration de chaque parametre de la fonction.
        for (Tokatt p : params) {
            int scope = p.scope;
            String paramName = p.name;
            Tokatt tok = tabSymboles.Lookup(paramName);

            if (tok != null && tok.scope == scope) {
                System.err.println("error: '" + paramName
                        + "' already declared in this scope.");
                System.exit(1);
            }

            tabSymboles.Insert(paramName, p);
            cod.append(Code.genVar(p));
        }
    }
    ^(
        CORPS s = statement { cod.append(s); }
    )
    {
        // Generation de "endfunc".
        tabSymboles.Leave_Scope();
        cod.append(new Inst3a(Inst3a.TAC_ENDFUNC, null, null, null));
    }
    )
;

proto returns [Code cod]
    @init { cod = new Code(); }
    :
    ^( PROTO_KW
        t = type s = IDENT
        p = param_list
    {
        String name = s.getText();
        int scope = tabSymboles.getScope();
        Tokatt fun = tabSymboles.Lookup(name); 

        if (fun != null && fun.scope == scope) {
            System.err.println("error: '" + name + "' already declared in this scope.");
            System.exit(1);
        }

        fun = new Tokatt(new TypeFunction(t), name, scope);
        fun.value = p.size();
        tabSymboles.Insert(name, fun);
    }
    )
;

param_list returns [ArrayList<Tokatt> params]
    @init { params = new ArrayList<Tokatt>(); }
    :
    ^( PARAM
        ( p = param { params.add(p); } )*
    )
;

type returns [Type typ]
    @init { typ = null; }
    : INT_KW { typ = TypeSystem.TypeInteger(); }
    | VOID_KW { typ = TypeSystem.TypeVoid(); }
;

param returns [Tokatt tok]
    @init { tok = null; }
    : i = IDENT
    {
        tok = new Tokatt(TypeSystem.TypeInteger(), i.getText(), tabSymboles.getScope());
        tok.setParam();
    }
    |
    ^( ARRAY
        i = IDENT
    {
        tok = new Tokatt(TypeSystem.TypePointer(), i.getText(), tabSymboles.getScope());
        tok.setParam();
    }
    )
;

statement returns [Code cod]
    @init { cod = new Code(); }
    :
    ^( ASSIGN_KW
        i = IDENT
    {
        String name = i.getText();
        Tokatt to = tabSymboles.Lookup(name);

        if (to == null) {
            System.err.println("error: '" + name + "' undeclared.");
            System.exit(1);
        }
    }
        e = expression
    {
        Type type = TypeSystem.CheckBinOp(to.type, e.type);

        if (type == TypeSystem.T_error) {
            System.err.println("error: cannot convert " + e.type + " to " + to.type);
        }

        cod.append(Code.genCopy(to, e));
    }
    )
    |
    ^( ASSIGN_KW
        t = tab_elem
        e = expression
    {
        cod.append(e.code);
        cod.append(Code.genVarTab(t, e.place));
    }
    )
    |
    ^( RETURN_KW
        e = expression
    {
        cod.append(Code.genReturn(e));
    }
    )
    |
    ^( PRINT_KW
        ( p = print_item
        {
            cod.append(p.code);

            if (p.type == TypeSystem.TypeText()) {
                cod.append(Code.genCall("L4", null));
            } else if (p.type == TypeSystem.TypeInteger()) {
                cod.append(Code.genCall("L2", null));
            }
        }
        )+
    )
    | ^( READ_KW
        ( r = read_item { cod.append(r); } )+
    )
    |
    /* If statement */
    ^(  IF_KW e = expression
    {
        Tokatt faux = SymbDistrib.newLabel();
        Tokatt fin = SymbDistrib.newLabel();

        cod.append(Code.genJumpIfz(e, faux));
    }
        s1 = statement
    {
        cod.append(s1);
        cod.append(Code.genGoto(fin));
        cod.append(Code.genLabel(faux));
    }
        ( s2 = statement
        {
            cod.append(s2);
        }
        )?
    {
        cod.append(Code.genLabel(fin));
    }
    )
    |
    /* While statement */
    ^(  WHILE_KW
        e = expression
    {
        Tokatt debut = SymbDistrib.newLabel();
        Tokatt fin = SymbDistrib.newLabel();

        cod.append(Code.genLabel(debut));
        cod.append(Code.genJumpIfz(e, fin));
    }
        s = statement
    {
        cod.append(s);
        cod.append(Code.genGoto(debut));
        cod.append(Code.genLabel(fin));
    }
    )
    |
    ^( FCALL_S
        i = IDENT
    {
        String name = i.getText();
        Tokatt fun = tabSymboles.Lookup(name);

        if (fun == null) {
            System.err.println("error: function '" + name + "' undeclared.");
            System.exit(1);
        }

        int nbArgs = 0;
    }
        ( e = expression
        {
            cod.append(e.code);
            Tokatt var = e.place;

            cod.append(Code.genArg(var));
            nbArgs++;
        }
        )*
    {
        if (nbArgs > fun.value) {
            System.err.println("error: call of function '" + name
                    + "' with too many arguments.");
            System.exit(1);
        }
        if (nbArgs < fun.value) {
            System.err.println("error: call of function '" + name
                    + "' with too few arguments.");
            System.exit(1);
        }

        cod.append(Code.genCall(fun, null));
    }
    )
    | b = block
    {
        cod.append(b);
    }
;

block returns [Code cod]
    @init { cod = new Code(); }
    :
    ^( BLOCK
    {
        tabSymboles.Enter_Scope();
    }
       d = declaration { cod.append(d); }
       ( s = suite_inst { cod.append(s); } )+
    {
        tabSymboles.Leave_Scope();
    }
    )
    | ^( BLOCK
        ( s = suite_inst { cod.append(s); } )+
    )
;

suite_inst returns [Code cod]
    @init { cod = new Code(); }
    :
    ^( INST
        ( s = statement { cod.append(s); } )+
    )
;

tab_elem returns [Tabatt tab]
    @init { tab = null; }
    :
    ^( ARELEM
       i = IDENT
       e = expression
    {
        String name = i.getText();
        Tokatt to = tabSymboles.Lookup(name);

        if (to == null) {
            System.err.println("error: '" + name + "' undeclared.");
            System.exit(1);
        }

        if (e.type != TypeSystem.TypeInteger()) {
            System.err.println("error: wrong table index value.");
            System.exit(1);
        }

        tab = new Tabatt(to.type, e.code, to, e.place); 
    }
    )
;

expression returns [Expratt exp]
    @init { exp=null; }
    : ^( PLUS exp1 = expression  exp2 = expression )
    {
        Type ty = TypeSystem.CheckBinOp(exp1.type, exp2.type);
        Tokatt temp = SymbDistrib.newTemp();
        Code cod = Code.genBinOp(Inst3a.TAC_ADD, exp1, exp2, temp);
        exp = new Expratt(ty, cod, temp);
    }
    | ^( MOINS exp1 = expression  exp2 = expression )
    {
        Type ty = TypeSystem.CheckBinOp(exp1.type, exp2.type);
        Tokatt temp = SymbDistrib.newTemp();
        Code cod = Code.genBinOp(Inst3a.TAC_SUB, exp1, exp2, temp);
        exp = new Expratt(ty, cod, temp);
    }
    | ^( MUL exp1 = expression  exp2 = expression )
    {
        Type ty = TypeSystem.CheckBinOp(exp1.type, exp2.type);
        Tokatt temp = SymbDistrib.newTemp();
        Code cod = Code.genBinOp(Inst3a.TAC_MUL, exp1, exp2, temp);
        exp = new Expratt(ty, cod, temp);
    }
    | ^( DIV exp1 = expression  exp2 = expression )
    {
        Type ty = TypeSystem.CheckBinOp(exp1.type, exp2.type);
        Tokatt temp = SymbDistrib.newTemp();
        Code cod = Code.genBinOp(Inst3a.TAC_DIV, exp1, exp2, temp);
        exp = new Expratt(ty, cod, temp);
    }
    | e = primaire
    {
        exp = e;
    }
;

primaire returns [Expratt exp]
    @init { exp = null; }
    : i = INTEGER
    {
        int ival = Integer.parseInt(i.getText());
        exp = new Expratt(TypeSystem.TypeInteger(), new Code(), new Tokatt(ival));
    }
    | s = IDENT
    {
       String name = s.getText();
       Tokatt to = tabSymboles.Lookup(name);

        if (to == null) {
            System.err.println("error: '" + name + "' undeclared.");
            System.exit(1);
        }

        exp = new Expratt(TypeSystem.TypeInteger(), new Code(), to);
    }
    | t = tab_elem
    {
        Tokatt tmp = SymbDistrib.newTemp();
        Code cod = Code.genTabVar(tmp, t);

        exp = new Expratt(TypeSystem.TypeInteger(), cod, tmp);
    }
    |
    ^( FCALL
        i = IDENT
    {
        String name = i.getText();
        Tokatt fun = tabSymboles.Lookup(name);

        if (fun == null) {
            System.err.println("error: function '" + name + "' undeclared.");
            System.exit(1);
        }

        Code cod = new Code();
        Tokatt ret = SymbDistrib.newTemp();
        cod.append(Code.genVar(ret));

        int nbArgs = 0;
    }
        ( e = expression
        {
            cod.append(e.code);
            Tokatt var = e.place;

            cod.append(Code.genArg(var));
            nbArgs++;
        }
        )*
    {
        if (nbArgs > fun.value) {
            System.err.println("error: call of function '" + name
                    + "' with too many arguments.");
            System.exit(1);
        }
        if (nbArgs < fun.value) {
            System.err.println("error: call of function '" + name
                    + "' with too few arguments.");
            System.exit(1);
        }

        cod.append(Code.genCall(fun, ret));

        exp = new Expratt(((TypeFunction)(fun.type)).ReturnType(), cod, ret);
    }
    )
;

print_item returns [Expratt exp]
    @init { exp = null; }
    : t = TEXT
    {
        String text = t.getText();
        Type ty = TypeSystem.TypeText();

        Tokatt to = new Tokatt(text);
        Code cod = Code.genArg(to.label);
        cod.appendData(to);

        exp = new Expratt(ty, cod, null);
    }
    | e = expression
    {
        Code cod = new Code();
        cod.append(e.code);
        cod.append(Code.genArg(e.place));

        exp = new Expratt(e.type, cod, e.place);
    }
;

read_item returns [Code cod]
    @init { cod = new Code(); }
    : i = IDENT
    {
        String name = i.getText();
        Tokatt to = tabSymboles.Lookup(name);

         if (to == null) {
             System.err.println("error: '" + name + "' undeclared.");
             System.exit(1);
         }

         cod.append(Code.genCall("L8", to));
    }
    | t = tab_elem
    {
        Tokatt to = tabSymboles.Lookup(t.place.name);

        if (to == null) {
            System.err.println("error: '" + t.place.name + "' undeclared.");
            System.exit(1);
        }

        Tokatt tmp = SymbDistrib.newTemp();
        cod.append(Code.genCall("L8", tmp));

        cod.append(Code.genVarTab(t, tmp));
    }
;

declaration returns [Code cod]
    @init { cod = new Code(); }
    :
    ^( DEC
        ( d = dec_item { cod.append(d); } )+
    )
;

dec_item returns [Code cod]
    @init { cod = new Code(); }
    : s = IDENT
    {
        int scope = tabSymboles.getScope();
        String name = s.getText();
        Tokatt to = tabSymboles.Lookup(name);

        if (to != null && to.scope == scope) {
            System.err.println("error: '" + name + "' already declared in this scope.");
            System.exit(1);
        }

        Tokatt tok = new Tokatt(TypeSystem.TypeInteger(), name, scope);

        tabSymboles.Insert(name, tok);
        cod.append(Code.genVar(tok));
    }
    |
    ^( ARDEC
        s = IDENT
        i = INTEGER
    {
        int scope = tabSymboles.getScope();
        String name = s.getText();
        Tokatt to = tabSymboles.Lookup(name);

        if (to != null && to.scope == scope) {
            System.err.println("error: '" + name + "' already declared in this scope.");
            System.exit(1);
        }

        int size = Integer.parseInt(i.getText());
        to = new Tokatt(new TypeArray(TypeSystem.TypeInteger(), size), name, scope);

        tabSymboles.Insert(name, to);
        cod.append(Code.genVar(to));
    }
    )
;
